package com.zoe.onelink.enhancer.init;

import cn.hutool.core.thread.ThreadUtil;
import com.zoe.onelink.enhancer.init.event.OnelinkInitEvent;
import com.zoe.onelink.enhancer.init.event.OnelinkInitResult;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-12-02
 */
@Slf4j
public class ApplicationInitializerInvoker implements ApplicationListener<ApplicationStartedEvent> {

    private final ApplicationContext applicationContext;

    private final List<ApplicationInitializer> initializers;
    /**
     * 是否已处理
     */
    private final AtomicBoolean processed = new AtomicBoolean(false);

    /**
     * 初始化器执行结果
     */
    private final Map<Class<? extends ApplicationInitializer>, Boolean> statusMap = new ConcurrentHashMap<>();

    /**
     * 初始化完成
     */
    private final AtomicBoolean completed = new AtomicBoolean(false);

    public ApplicationInitializerInvoker(ApplicationContext applicationContext,
                                         List<ApplicationInitializer> initializers) {
        this.applicationContext = applicationContext;
        this.initializers = initializers;
        initializers.forEach(applicationInitializer -> this.statusMap.put(applicationInitializer.getClass(), false));
    }

    @SneakyThrows
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        // 是否已经处理过(多个容器加载的情况下事件会被多次刷新)
        if (this.processed.getAndSet(true)) {
            return;
        }
        if (initializers == null || initializers.size() == 0) {
            return;
        }
        initializers.sort(Comparator.comparingInt(ApplicationInitializer::getOrderIndex));

        log.info("[应用初始化事件] 共找到{}个初始化事件,开始初始化...", initializers.size());
        long start = System.currentTimeMillis();
        OnelinkInitResult onelinkInitResult = new OnelinkInitResult();
        for (ApplicationInitializer initializer : initializers) {
            // 异步初始化
            if (initializer.isAsync()) {
                this.initWithAsync(initializer, event, onelinkInitResult);
            } else {
                this.init(initializer, event, onelinkInitResult);
            }
        }
        long end = System.currentTimeMillis();
        log.info("[应用初始化事件] 初始化完成,总耗时:{}ms", end - start);
    }

    /**
     * 同步方式初始化
     */
    private void init(ApplicationInitializer initializer, ApplicationStartedEvent event, OnelinkInitResult initResult) throws Exception {
        if (!initializer.preInit(event)) {
            return;
        }
        try {
            initializer.init(event);
            this.finish(initializer, initResult);
        } catch (Exception e) {
            initResult.addFailureEx(e);
            if (initializer.throwable()) {
                throw e;
            } else {
                log.error("应用程序初始化失败:[{}]}", initializer.getClass().getName(), e);
            }
        }

    }

    /**
     * 异步方式初始化
     */
    private void initWithAsync(ApplicationInitializer initializer, ApplicationStartedEvent event, OnelinkInitResult initResult) {
        ThreadUtil.execute(() -> {
            try {
                this.init(initializer, event, initResult);
                this.finish(initializer, initResult);
            } catch (Exception e) {
                initResult.addFailureEx(e);
                log.error("应用程序初始化失败:[{}] - 原因:{}", initializer.getClass().getName(), e);
            }
        });
    }

    /**
     * 初始化完成
     */
    private void finish(ApplicationInitializer applicationInitializer, OnelinkInitResult initResult) {
        synchronized (ApplicationInitializerInvoker.class) {
            this.statusMap.put(applicationInitializer.getClass(), true);
            // 整体任务已完成或者包含未完成任务
            if (this.completed.get() || statusMap.containsValue(false)) {
                return;
            }
            this.completed.set(true);
            this.publishCompletedEvent(initResult);
        }
    }

    private void publishCompletedEvent(OnelinkInitResult initResult) {
        try {
            log.debug("[应用初始化事件] 发布初始化完成事件");
            this.applicationContext.publishEvent(new OnelinkInitEvent(initResult));
        } catch (Exception e) {
            log.warn("发布初始化完成事件失败", e);
        }
    }
}
